var pow_bc_root_directory = pow_bc_get_root_directory();

// PUBLIC API //

/*
 *
 * Reads a file, binary by default.
 *
 */

function pow_file( readfile ) {
	try {
		var read_flags = "rb";
		var offset = 0;
		var length = 0;
		var absolute_path = "";
		var file_contents = "";
		if(readfile.match(/^\//)) {
			absolute_path = readfile;
		} else if(readfile.match(/^http:\/\//)) {
			file_contents = pow_download_file(readfile);
			return file_contents;
		} else {
			absolute_path = pow_bc_root_directory+pow_request.req_dir+"/"+readfile;
		}
		file_contents = pow_get_file_at_offset( absolute_path, 
				offset, length, read_flags);
		// Content-Length is normally used for images
		file_contents = 
				file_contents.replace(/(.|\n|\r)*Content-Length: \d+\r\n\r\n/m,"");
		file_contents = 
				file_contents.replace(/Content-Location: .*\r\n\r\n/m,"");
		return file_contents;
	} catch (e) {
		log_error(e);
		return "";
	}
}

/*
 *
 * Returns a single line, depending on line separator
 *
 */

function pow_file_line(fstream) {
	try {
  var line = {}
  var hasmore = fstream.readLine(line);
	//if(line == undefined) { return null; }
  if( !line.value && !hasmore ) { return null; }
  var simple_line = line.value;
  return simple_line + "\n";
	} catch (e) {
		log_error(e);
	}
}

/*
 *
 * Closes a line input stream
 *
 */

function pow_file_close(fstream) {
	try {
  fstream.close();
	} catch (e) {
		log_error(e);
	}
}

/*
 *
 * Opens a line input stream
 *
 */

function pow_file_open(filename, rwa_flags) {
	try {
  	var file = pow_file_get_handle(filename);
		if( ! file.exists()) {
			return;
		}
		var write_flags = 0x01;
		if(rwa_flags == "w") {
			write_flags = 0x02 | 0x08 | 0x20;
			fstream = Components.classes["@mozilla.org/network/file-output-stream;1"]
               .createInstance(Components.interfaces.nsIFileOutputStream);
  		fstream.init(file, write_flags, 0644, 0);
		} else {
			// "r"
			write_flags = 0x01;
  		fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                .createInstance(Components.interfaces.nsIFileInputStream);
  		fstream.init(file, write_flags, 0, 0);
  		fstream.QueryInterface(Components.interfaces.nsILineInputStream);
		}
  	return fstream;
	} catch (e) {
		log_error(e);
	}
}


/*
 *
 * Writes contents to a file using flags.
 * w - write ascii
 * wb - write binary
 *
 */

function pow_file_put_contents(filename, contents, rwa_flags) {
	try {
		var offsets = new Array();
		var data = new Array();
		data.push(contents);
		offsets.push(0);
		var absolute_path = "";
		if(filename.match(/^\//)) {
			absolute_path = filename;
		} else {
			absolute_path = pow_bc_root_directory+pow_request.req_dir+"/"+filename;
		}
		pow_save_file_at_offset (absolute_path, data, offsets, rwa_flags);
	} catch (e) {
		log_error(e);
	}
	return;
}

/*
 *
 * Creates a directory relative to the sjs script.
 *
 */

function pow_file_mkdir(dirname) {
	pow_mkdir(dirname);
}

/*
 *
 * Deletes a file.
 *
 */

function pow_file_delete(filename) {
	try {
		pow_delete_file(filename, false);
	} catch (e) {
		log_error(e);
	}
	return;
}

/*
 *
 * Downloads a file from the web.
 *
 */

function pow_download_file(url) {
	var page = pow_remote_request(url,"GET","");
	return page;
}

/*
 *
 * Returns boolean existence of a file.
 *
 */

function pow_file_exists(filename) {
	try {
		var fh = pow_file_get_handle(filename);
		if(fh.exists()) {
			return true;	
		} else {
			return false;
		}
	} catch(e) {
		log_error(e);
		return false;
	}
}

/*
 *
 * Executes a command line script (PUBLIC)
 *
 */

function pow_exec(path, args, blocking) {
	try {
		var res;
	  var script_handle = pow_file_get_handle(path);
	  var root_dir = pow_file_get_handle("/");
	  var pipe_args = new Array(script_handle.path, 
			script_handle.parent.path, root_dir.path);
		pipe_args = pipe_args.concat(args);
		if(navigator.oscpu.match(/Windows/)) {
				res = pow_pipe_exec("/pow/bin/makepipe.bat", pipe_args, blocking);
		} else {
				res = pow_pipe_exec("/pow/bin/makepipe.sh", pipe_args, blocking);
		}
	  return res;
	} catch(e) {
		log_error(e);
	}
}

/*
 *
 * Executes a command line script. (PRIVATE)
 * Root is POW root
 *
 */

function pow_pipe_exec(filename, args, blocks) {
	try {
		var f = pow_file_get_handle(filename);
 	 	if ( f.exists() == false ) {
			return "";
		}
		var runner = Components.classes["@mozilla.org/process/util;1"].
				createInstance(Components.interfaces.nsIProcess);
		runner.init(f);
		runner.run(blocks, args, args.length);
		pow_server.EXIT_VALUE = runner.exitValue;
		var text = pow_file("/pow/data/powpipe.txt");
		return text;
	} catch(e) {
		log_error(e);
	}
	return;
}

/*
 *
 * Returns /pow/data
 *
 */

function pow_file_get_data_dir() {
	try {
		return pow_bc_get_data_directory();
	} catch (e) {
		log_error(e);
		return "";
	}
}

/*
 *
 * Returns /pow/log
 *
 */

function pow_file_get_log_dir() {
	try {
		return pow_bc_get_log_directory();
	} catch (e) {
		log_error(e);
		return "";
	}
}

/*
 *
 * Returns /pow/htdocs
 *
 */

function pow_file_get_docs_dir() {
	try {
		return pow_bc_get_root_directory();
	} catch (e) {
		log_error(e);
		return "";
	}
}

/*
 *
 * Database object.
 *
 */

function pow_DB(db_name) {

 /*
  *
  * Creates database if it does not exist.
  * Holds connection open.
  *
  */

	this.open = function( db_name ) {
		try {
			var new_file =  pow_file_get_handle("/");
	 		var data_dir = pow_bc_get_data_directory();
	 		new_file = pow_append_path(new_file, data_dir+"/");
 	 		new_file.append(db_name+".sqlite");
 	 		var storageService = Components.classes["@mozilla.org/storage/service;1"]
 	                    .getService(Components.interfaces.mozIStorageService);
 	 		var mDBConn = storageService.openDatabase(new_file);
 	 		return mDBConn;
	 	} catch (e) {
			log_error(e);
			return "";
	 	}
	};

 /*
  *
  * Deletes database file.
  *
  */

	this.drop_database = function( db_name ) {
		try {
			this.conn = null; // Added for XP
	 		var data_dir = pow_bc_get_data_directory();
			pow_delete_file(data_dir+"/"+db_name+".sqlite", false);
	  } catch (e) {
			log_error(e);
	  }
		return;
	};

 /*
  *
  * Connection object. Should be closed later.
  *
  */

	this.conn = this.open(db_name);

 /*
  *
  * Prints a table from the results object.
  *
  */

	this.pretty_print = function(results) {
		try {
		var table;
	  table = "<pre>";
 	  var separator = "";
		var this_row = new Array();
 	  for(var j=0; j < results["column_names"].length; j++) {
 	   for(var k=0;k < results["column_widths"][j]; k++) {
 	     if(j || k) {
 	       separator += "-";
 	     } else {
 	       separator += " ";
 	     }
 	   }
 	   separator += "---";
 	 	}
 	 	separator += "\n";
	
 	 	table += "\n";
 	 	table += separator;
 	 	for(var i=-1; i < results.length; i++) {
 	   table += "| ";
 	   if(i==-1) {
 	     this_row = results["column_names"];
 	   } else {
 	     this_row = results[i];
 	   }
		 var column_row_length = 0;
 	   for(var j=0; j < this_row.length; j++) {
			 if(this_row[j] == null ) {
					column_row_length = 4;
			 } else {
					column_row_length = this_row[j].length;
			 }
 	     for(var k=0;k < results["column_widths"][j]-column_row_length; k++) {
 	      table += " ";
 	     }
 	     table += this_row[j];
 	     table += " | ";
 	   }
 	   table += "\n";
 	   table += separator;
 	 	}
 	 	table += "</pre>";
		pow_response.script_out += table;
		} catch(e) {
			log_error(e);
		}
		return;
	};

 /*
  *
  * Executes database SQL call.
  *
  */
	
	this.exec = function(statement) {
		try {
			var row = new Array();
			var column_names = new Array();
			var column_widths = new Array();
			var prep_statement = this.conn.createStatement(statement);
			if(!prep_statement) { return ""; }
			prep_statement.execute();
			var count = 0;
			if(statement.match(/SELECT/i)) {
				while(prep_statement.executeStep()) {
					row[count] = new Array();
					if(!count) {
						for(var i=0;i<prep_statement.columnCount;i++) {
							var name = prep_statement.getColumnName(i);
							column_widths[i] = name.length;
							column_names[i] = name;
						}
					}
					for(var i=0;i<prep_statement.columnCount;i++) {
						var data = prep_statement.getUTF8String(i);
						if(data && data.length > column_widths[i]) {
							column_widths[i] = data.length;
						}
						row[count].push(data);
					}
					count++;
				}
			}
			row["column_names"] = column_names;
			row["column_widths"] = column_widths;
			return row;
		} catch(e) {
			log_error(e);
			return "";
		}
	};
	// I DO not know what this does
	this.reset = function(statement) {
		statement.reset();
	}
}

/*
 * Returns nsIFile object array
 */

function pow_file_list_dir(dirname) {
	var dir_list = new Array();
  var f =  pow_file_get_handle(dirname);
  if(f.exists() && f.isDirectory() ) {
 	 var dir_stack = new Array(f);
 	 while( dir_stack.length ) {
 	   var entries = dir_stack.pop().directoryEntries;
 	   while(entries.hasMoreElements() ) {
 	     var entry = entries.getNext();
 	     entry.QueryInterface(Components.interfaces.nsIFile);
				dir_list.push(entry);
 	   }
 	 }
  } else if(f.exists()) { // file 
		dir_list.push(f);
	}
	return dir_list;
}

/*
 *
 * pow_remote_post()
 * POST sends key value pairs to server
 *
 */

function pow_remote_post(url,params) {
	var method = "POST";
	var page = pow_remote_request(url, method, params);
	return page;
}

/*
 *
 * pow_remote_put()
 * sends PUT and key value pairs to server
 *
 */

function pow_remote_put(url,params) {
	var method = "PUT";
	var page = pow_remote_request(url, method, params);
	return page;
}

/*
 *
 * pow_remote_head()
 * sends HEAD request to server
 *
 */

function pow_remote_head(url,params) {
	var method = "HEAD";
	var page = pow_remote_request(url, method, params);
	return page;
}

/*
 *
 * pow_remote_delete()
 * sends DELETE request to server
 *
 */

function pow_remote_delete(url,params) {
	var method = "DELETE";
	var page = pow_remote_request(url, method, params);
	return page;
}

// END PUBLIC API //

function pow_remote_request(url, method, params) {
  var page = "";
  var req = new XMLHttpRequest();
  req.overrideMimeType('text/plain; charset=x-user-defined'); // binary
  if(req) {
    req.open(method, url, false);
		if(method == "POST") {
			req.setRequestHeader("Content-type","application/x-www-form-urlencoded");
			req.setRequestHeader("Content-length", params.length);
			req.setRequestHeader("Connection", "close");
		}
    req.send(params);
    if (req.status == 200) {
			if(method == "HEAD") {
				return req.getAllResponseHeaders();
			}
      page = req.responseText;
			req.getResponseHeader("Content-Type")
    } else {
      return "";
    }
  }
  return page;
}

/**
 *
 * Returns an array of all files matching regex
 *
 */

function pow_file_find_matching ( dirname , regex ) {
  var file_list = new Array();
  var find_regex = new RegExp(regex);
  var dir_stack = new Array(pow_get_nsifile(dirname));
	var testit = pow_get_nsifile("/breadcrumbs");
  while( dir_stack.length ) {
    var entries = dir_stack.pop().directoryEntries;
    while(entries.hasMoreElements() ) {
      var entry = entries.getNext();
      entry.QueryInterface(Components.interfaces.nsIFile);
      if( entry.isDirectory() ) {
        dir_stack.push(entry);
      } else {
        if(entry.leafName.match(find_regex)) {
          var end_file = entry.path.replace( /.*(\/breadcrumbs\/pages)/,
              "/breadcrumbs/pages");
          file_list.push(end_file);
        }
      }
    }
  }
  return file_list;
}

/**
 *
 * POSTs a file, filename to location url
 *
 */

function pow_upload_file(url, filename) {
	try {
	var port = 80;
	var timeout = 5;
	var leafname = "";
	var leafname_m = filename.match(/([^\/]*)$/);
	if(!leafname_m) {
		return "";
	}
	leafname = leafname_m[1];
	var file_contents = pow_file(filename);
	if(!file_contents) {
		return "";
	}
	url_parts = url.match(/http:\/\/([^:\/]+)(:\d+)?(.*)/);
	if(url_parts) {
		if(url_parts[2]) {
			port = url_parts[2];
		}
		var delimiter = "---------------------------168072824752491622650073";
		var whole_file = 
			"--"+delimiter+"\r\n"
			+"Content-Disposition: form-data; name=\"MAX_FILE_SIZE\"\r\n"
			+"\r\n"
			+"100000\r\n"
			+"--"+delimiter+"\r\n"
			+"Content-Disposition: form-data; name=\"userfile\"; filename=\""+
					leafname+"\"\r\n"
			+"Content-Type: text/plain\r\n"
			+"\r\n"
			+file_contents
			+"\r\n"
			+"--"+delimiter; 

		var query = "POST "+url_parts[3]+" HTTP/1.1\r\n"
			+"Host: localhost:6670\r\n"
			+"User-Agent: POW/0.0.5\r\n"
			+"Accept: text/xml,application/xml,application/xhtml+xml,"
			+"text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n"
			+"Accept-Language: en-us,en;q=0.5\r\n"
			+"Accept-Encoding: gzip,deflate\r\n"
			+"Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
			+"Keep-Alive: 300\r\n"
			+"Connection: keep-alive\r\n"
			+"Referer: http://localhost/~davidkellogg/test/upload.php\r\n"
			+"Content-Type: multipart/form-data; boundary="+delimiter+"\r\n"
			+"Content-Length: "+whole_file.length+"\r\n"
			+"\r\n"
			+whole_file+"--";
		var page = pow_read_from_socket(url_parts[1],port, query, timeout);
		return page;
	} else {
		return "";
	}
	} catch(e) {
		log_error(e);
		return "";
	}
}

/**
 *
 * Returns handle for root where /pow and /extensions are located
 * Or gives user-defined location.
 *
 */

function pow_file_get_root_handle() {
	try {
		var file = pow_file_get_handle("/");
		file = pow_append_path(file, pow_bc_get_root_directory());
		return file.path;
	} catch (e) {
		log_error(e); return "";
	}
}

/**
 *
 * Returns string from socket read from host:port given a timeout
 * outputData is a raw 'GET ... \r\n\r\n' or POST with data
 *
 */

function pow_read_from_socket(host, port, outputData, timeout) {
  try {
		var output = "";
    var transportService =
      Components.classes["@mozilla.org/network/socket-transport-service;1"]
        .getService(Components.interfaces.nsISocketTransportService);
    var transport = transportService.createTransport(null,0,host,port,null);

    var outstream = transport.openOutputStream(1,0,0);
		// TRY Seems to work well
    var bos = Components.classes["@mozilla.org/binaryoutputstream;1"].  
		createInstance(Components.interfaces.nsIBinaryOutputStream);
    bos.setOutputStream(outstream);
		// END TRY
    bos.write(outputData,outputData.length);

		transport.setTimeout(1, timeout);
    var stream = transport.openInputStream(1,100,100000);

		var bis = Components.classes["@mozilla.org/binaryinputstream;1"]
  	    .createInstance( Components.interfaces.nsIBinaryInputStream );
		bis.setInputStream(stream);

    var instream = Components.classes["@mozilla.org/scriptableinputstream;1"]
      .createInstance(Components.interfaces.nsIScriptableInputStream);
    instream.init(stream);
		var output = "";
		var size = 0;
		var i;
		// Blocking first byte
		output = bis.readBytes(1);
		for(i = 0;i<1000000;i++) {
			if(instream) {
				try {
					size = instream.available();
				} catch (e) {
					instream.close();
					outstream.close();
					bis.close();
					return null;
				}
			}
			if(size) {
		 		output += bis.readBytes(size);
				break;
			}
		}
		try {
			var trash = "";
			for(var i=0;i<10000000;i++) {
				output += bis.readBytes(1);
			}
		} catch(e) {
			// Not enough data
		}
		bis.close();
		instream.close();
		outstream.close();
		return output;
  } catch (e){
		log_error(e);
  }
  return null;
}

// CLOBBER IS NOT IMPLEMENTED

/**
 *
 * Copies a file from source to dest
 * If clobber is false, the file cannot be overwritten
 *
 */

function pow_copy_file (source, dest, clobber) {
	try {
	if( ! source || ! dest ) {
		return ""; // empty string
	}
	var m = source.match(/([^\/]+)$/);
	var source_file = m[1];
	var from; // FROM is an immutable dir location
	var dirservice = 
		Components.classes["@mozilla.org/file/directory_service;1"].
		getService(Components.interfaces.nsIProperties);
	if(source.match(/^__DEFAULT__/)) { // FF mother dir
		source = source.replace(/^__DEFAULT__/,"");
		from = dirservice.get('ProfD', Components.interfaces.nsILocalFile);
	} else if(source.match(/^__DEFAULT_XR__/)) { // XULRunner dir
		source = source.replace(/^__DEFAULT_XR__/,"");
		from = dirservice.get('resource:app', Components.interfaces.nsILocalFile);
	} else {
		from = pow_file_get_handle("/"); // From default or user-specified root dir
	}
	from = pow_append_path(from, source);
  if ( from.exists() == false ) {
		return "";
	}
	// serv_dir_file = dest;
	var to = pow_file_get_handle("/"); // root
	var to_check = pow_file_get_handle("/");
	to = pow_append_path(to, dest);
	to_check = pow_append_path(to_check, dest+"/"+source_file);
  if ( to_check.exists() == true && clobber == false ) { // to file exists
		return ""; // cannot clobber this file
	}
	if( to_check.exists() ) {
		to_check.remove(false);
	}
	if( ! to_check.exists() ) {
		from.copyTo(to, "");
	}
	} catch (e) {
		log_error(e); return "";
	}
}

/**
 *
 * Saves contents in filename using rwa_flags
 * Acceptable flags include wb (write binary), wba (write binary append)
 *
 */

function pow_save_file (filename, contents, rwa_flags) {
	if( ! filename ) {
		return "";
	}
	var offsets = new Array();
	var data = new Array();
	data.push(contents);
	offsets.push(0);
	pow_save_file_at_offset(filename, data, offsets, rwa_flags);
}

/**
 *
 * Returns file size
 * Useful for binary data
 *
 */

function pow_file_get_size(filename) {
	try {
		var fh = pow_file_get_handle(filename);
		if(! fh.exists()) {
			return 0;
		}
		return fh.fileSize;
	} catch(e) {
		log_error(e); return "";
	}
}

function pow_file_get_handle(filename) {
	try {
		var use_default = false;
		var absolute_path = "";
		if( ! filename ) {
			return "";
		}
		if(filename.match(/^\//)) {
			absolute_path = filename;
		} else if (filename.match(/^__DEFAULT__/)) {
			filename = filename.replace(/^__DEFAULT__/,"");
			absolute_path = filename;
			use_default = true;
		} else {
			pow_bc_root_directory = pow_bc_get_root_directory();
			absolute_path = pow_bc_root_directory+pow_request.req_dir+"/"+filename;
		}
		var user_root_dir = get_pow_root_dir();
		var f;
		// CHOOSE user root dir or default
		if(user_root_dir && !use_default) {
			f = Components.classes["@mozilla.org/file/local;1"]
		     .createInstance(Components.interfaces.nsILocalFile);
		 	f.initWithPath(user_root_dir);
		} else {
			var dirservice = 
					Components.classes["@mozilla.org/file/directory_service;1"].
					getService(Components.interfaces.nsIProperties);
			var f = dirservice.get('ProfD', Components.interfaces.nsILocalFile);
		}
		f = pow_append_path(f, absolute_path);
		return f;
	} catch(e) {
		log_error(e); return "";
	}
}

function pow_save_file_at_offset (filename, data, offsets, rwa_flags) {
	if( ! filename ) {
		return "";
	}
	if( ! rwa_flags ) {
		rwa_flags = "wb";
	}
	var f = pow_file_get_handle(filename);

  if ( f.exists() == false ) {
		try {
			f.create( Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0644 );
		} catch (e) {
			log_error("failed writing");
		}
	} else if(rwa_flags && offsets[0] == 0 && 
				offsets.length == 1 && 
				rwa_flags.match(/^wb?$/)) {
		f.remove(false);
	}
	// SAVE file
	var write_flags = 0x02 | 0x08
	// overwrite
  if(rwa_flags == "w") {
		write_flags = 0x02 | 0x08 | 0x20;
	// append
	} else if(rwa_flags == "wa") {
		write_flags = 0x02 | 0x10;
	// write binary
	} else if(rwa_flags == "wb") {
		write_flags = 0x02 | 0x08 | 0x20;
	// write binary append
	} else if(rwa_flags == "wba") {
		write_flags = 0x02 | 0x10;
	// read and write
	} else if(rwa_flags == "ws") {
		write_flags = 0x04 | 0x08;
	// read and write
	} else if(rwa_flags == "ws+") {
		write_flags = 0x04 | 0x08;
	}
	try {
		if( (rwa_flags == "ws" || rwa_flags == "ws+")  && offsets[0] > 0 ) {

			if(rwa_flags == "ws+") {
				var sis = Components.classes["@mozilla.org/scriptableinputstream;1"]
     	 		 .createInstance( Components.interfaces.nsIScriptableInputStream );
				var is = Components.classes["@mozilla.org/network/file-input-stream;1"].
						createInstance( Components.interfaces.nsIFileInputStream );
				is.init( f, write_flags, 0644, 0 );
				is.QueryInterface(Components.interfaces.nsISeekableStream);
				if(offsets[0] > 0) {
					is.seek(0, offsets[0]);
				}
    		sis.init( is );
				var appendix = "";
				if(sis.available() < 10000000) {
    			appendix = sis.read( sis.available() );
				}
    		data[0] += appendix;
				is.close();
				sis.close();
			}
		}
		var outputStream = 
				Components.classes["@mozilla.org/network/file-output-stream;1"].
				createInstance( Components.interfaces.nsIFileOutputStream );
		outputStream.init( f, write_flags, 0644, 0 );

		for (var i=0 ;i < offsets.length;i++) {
			if(offsets[i] > 0) {
				outputStream.QueryInterface(Components.interfaces.nsISeekableStream);
				outputStream.seek(0, offsets[i]);
			}
			var result = outputStream.write( data[i], data[i].length );
		}
		outputStream.close();
	} catch(e) {
		log_error(e); return "";
	}
}

function pow_mkdir(dirname) {
	if( ! dirname ) {
		return "";
	}
	var f = pow_file_get_handle(dirname);
  if ( f.exists() == false ) {
		try {
			f.create( Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755 );
		} catch (e) {
			log_error(e); return "";
		}
	}
}

function pow_delete_file (filename, recursive) {
	try {
	if(!recursive) {
		recursive = false;
	}
	// User profile directory
		var f = pow_file_get_handle(filename);
  if ( f.exists() == false ) {
		return "";
	}
	f.remove(recursive);
  } catch (e) {
     log_error(e); return "";
  }
}

function pow_get_file( readfile, read_flags ) {
	try {
	var offset = 0;
	var length = 0;
	var file_contents = pow_get_file_at_offset( readfile, offset, length, read_flags );
	file_contents = 
			file_contents.replace(/Content-Length: \d+\r\n\r\n/m,"");
	return file_contents;
	} catch (e) {
		log_error(e); return "";
	}
}

function pow_get_nsifile(readfile) {
	// A LITTLE SECURITY
	readfile = readfile.replace(/\.{2,}/g,"");
	var display_path = readfile;
	var internal_path = readfile;

	display_path = display_path.replace(/^\/breadcrumbs\/htdocs\//, "");
	display_path = display_path.replace(/^\/pow\/htdocs\//, "");
	internal_path = internal_path.replace(/htdocs\/breadcrumbs\//, "");
	internal_path = internal_path.replace(/htdocs\/pages\//, "pages/");
	display_path = display_path.replace(/^\//, "");
	content_location = "/"+display_path;

	try {
		dir_path = internal_path;
		var f_base =  pow_file_get_handle("/");
		var f;

		if( internal_path.match(/\/$/) ) {
			// A DIRECTORY
			file_path = dir_path + "index.sjs";
			f = pow_append_path(f_base, file_path);
			if( f.exists() ) {
				content_location += "index.sjs";
			} else {
				file_path = dir_path + "index.html";
				f_base =  pow_file_get_handle("/");
				f = pow_append_path(f_base, file_path);
				if( f.exists() ) {
					content_location += "index.html";
				} else {
					file_path = dir_path;
					f_base =  pow_file_get_handle("/");
					f = pow_append_path(f_base, file_path);
				}
			}
		} else {
			f_base =  pow_file_get_handle("/");
			f = pow_append_path(f_base, internal_path);
		}
	} catch (e) {
		log_error(e); return "";
	}
	return f;
}

function pow_get_file_at_offset( readfile, offset, length, read_flags ) {
	// A LITTLE SECURITY
	readfile = readfile.replace(/\.{2,}/g,"");
	var dir_path = "";
	var file_path = "";
	var content_location = "";
	if(!read_flags) { return "ERROR: File not found"; }

	var display_path = readfile;
	var internal_path = readfile;

	display_path = display_path.replace(/^\/breadcrumbs\/htdocs\//, "");
	display_path = display_path.replace(/^\/pow\/htdocs\//, "");
	internal_path = internal_path.replace(/htdocs\/breadcrumbs\//, "");
	internal_path = internal_path.replace(/htdocs\/pages\//, "pages/");
	display_path = display_path.replace(/^\//, "");
	content_location = "/"+display_path;
	dir_path = internal_path;

	// FEED default page for dir
	try {
		dir_path = internal_path;
		var f_base =  pow_file_get_handle("/");
		var f;
		if( internal_path.match(/\/$/) ) {
			// A DIRECTORY
			file_path = dir_path + "index.sjs";
			f = pow_append_path(f_base, file_path);
			if( f.exists() ) {
				content_location += "index.sjs";
			} else {
				file_path = dir_path + "index.html";
				f_base =  pow_file_get_handle("/");
				f = pow_append_path(f_base, file_path);
				if( f.exists() ) {
					content_location += "index.html";
				} else {
					file_path = dir_path;
					f_base =  pow_file_get_handle("/");
					f = pow_append_path(f_base, file_path);
				}
			}
		} else {
			f_base =  pow_file_get_handle("/");
			f = pow_append_path(f_base, internal_path);
		}
	} catch (e) {
		log_error(e); return "";
	}

	if( dir_path ) {
		try {
			if( f.exists() == false) { 
				return "ERROR: File or Directory Not Found";
			}
			var file_list_out = "";
			var prepend_breadcrumbs = "<a href='/"+display_path;
			var append_breadcrumbs = "'>";
			var file_list_out = "";
			if(pow_bc_root_directory.match(/breadcrumbs/)) {
				file_list_out += "<br><br><center>"+pow_get_search_form('')+"</center>";
			}
			file_list_out += "<br><br><br><br>\n";
			//  DIR
			if(f.isDirectory()) {
				if(content_location.match(/[^\/]$/)) {
					content_location += "/";
				}
				var entries = f.directoryEntries;
				var file_array = [];
				while(entries.hasMoreElements()) {
				  var entry = entries.getNext();
				  entry.QueryInterface(Components.interfaces.nsIFile);
				  file_array.push(entry);
					if(entry.isDirectory() ) {
						file_list_out += prepend_breadcrumbs+entry.leafName+"/"+
							append_breadcrumbs+entry.leafName+"/</a><br>\n";
					} else if(entry.isFile()) {
						file_list_out += prepend_breadcrumbs+entry.leafName+
							append_breadcrumbs+entry.leafName+"</a><br>\n";
					}
				}
				return "Content-Location: "+content_location+"\r\nContent-Length: "+
						file_list_out.length+"\r\n\r\n"+file_list_out;
			}
		} catch (e) {
			log_error(e); return "";
		}
	} else {
		if( f.exists() == false) { 
			return "ERROR: File Not Found";
		}
	}

	try {
		// OPEN stream
		var is = Components.classes["@mozilla.org/network/file-input-stream;1"].
				createInstance( Components.interfaces.nsIFileInputStream );

		var output;
		if( read_flags != "rb") {
		// ASCII file
			var sis = Components.classes["@mozilla.org/scriptableinputstream;1"]
       .createInstance( Components.interfaces.nsIScriptableInputStream );
			if(offset > 0) {
				is.QueryInterface(Components.interfaces.nsISeekableStream);
			}
			is.init( f,0x01, 00004, 0);
    	sis.init( is );
			if(offset > 0 || length > 0) {
				if(offset > 0) {
					is.seek(0, offset);
				}
				output = "\r\nContent-Length: "+f.fileSize+"\r\n\r\n";
				if(length > sis.available()) {
    			output += sis.read( sis.available() );
				} else {
    			output += sis.read( length );
				}
			} else {
				output = "\r\nContent-Length: "+f.fileSize+"\r\n\r\n"+
    		sis.read( sis.available() );
			}
			is.close();
			sis.close();
		} else {
			// BINARY file
			var data = {};
			is.init(f, 1, 0, false);
			var bis = Components.classes["@mozilla.org/binaryinputstream;1"]
   	    .createInstance( Components.interfaces.nsIBinaryInputStream );
			bis.setInputStream(is);
			// GET Bytes
			output = "\r\nContent-Length: "+f.fileSize+"\r\n\r\n"+
					bis.readBytes(f.fileSize);
			bis.close();
			is.close();
		}
	} catch (e) {
		log_error(e); return "";
	}
	output = "Content-Location: "+content_location+"\r\n" + output;
	return output;
}

function pow_append_path (file, path) {
	try {
	var dirs = new Array();
//	var num = 0;
	while( path.match(/^[^\/]*\/.* ?/) ) {
		var next_dir = path.replace(/^([^\/]*)\/(.*)/,"$1");
		file.append(next_dir);
		path = path.replace(/^([^\/]*)\/(.*)/,"$2");
	}
	if ( path ) {
		file.append(path);
	}
	return file;
	} catch(e) {
		log_error(e); return "";
	}
}

/*
 *
 * Logs messages and rotates logs
 *
 */

function pow_log(msg, filename, rot_filename) {
//	try {
	var size = pow_file_get_size(filename);
	if(size > 1000) {
		var old_log = pow_file(filename, msg+"\n");
		pow_file_put_contents(rot_filename, old_log);
		pow_save_file(filename, msg+"\n", "w");
	} else {
		pow_save_file(filename, msg+"\n", "wa");
	}
/*
	} catch(e) {
	}
*/
}

/*
 *
 * Logs errors and rotates logs
 *
 */

function log_error(e) {
//	try {
	var root = pow_bc_get_index_directory();

	var date_string = pow_get_pretty_date();
	var error_msg = date_string + e.fileName+" "+e.lineNumber+": "+e.message;
	pow_log(error_msg,root+"/log/error.txt",root+"/log/error.1.txt");
/*
	} catch(e) {
	}
*/
}

	/*
   *
   * Returns formatted date
   *
	 */

	function pow_get_pretty_date() {
    var date = new Date();
    var year = date.getYear()+1900;
    var month = date.getMonth()+1;
    var day = date.getDate();
    var hours = date.getHours();
    var minutes = date.getMinutes();
    var seconds = date.getSeconds();

    var date_string = year+"-0"+month+"-0"+day + " 0"+hours+":0"+
				minutes+":0"+seconds;
		date_string = date_string.replace(
			/(\d+)-\d*(\d{2})-\d*(\d{2}) \d*(\d{2}):\d*(\d{2}):\d*(\d{2})/,
			"$1-$2-$3 $4:$5:$6 ");
		return date_string;
	}

/*
 *
 * pow_session_end() 
 * Called by system if session was initiated
 *
 */

function pow_session_end() {
	var my_db = pow_session_init_db();
	var data = "";
	for (var i in pow_server.SESSION) {
		if(data) { data += ";"; }
		data += i+"="+pow_server.SESSION[i];
	}
	var expires_str = "";
	var random_cookie = pow_server.SESSION_ID;
	pow_session_add_data(my_db, random_cookie, expires_str, data);
}

/*
 *
 * pow_session_init() 
 * Called by user to initialize session
 *
 */

function pow_session_init(expires_delta) {
	if(!expires_delta) {
		expires_delta = 3600*24;
	}
	var my_db = pow_session_init_db();
	// INIT SESSION var
	if(!pow_server.SESSION) {
		pow_server.SESSION = new Array();
	}
	// END var

	// CREATE COOKIE
	var cook = pow_server.REQUEST_HEADERS['Cookie']
	if(!cook) {
		var now = new Date();
		var expires = new Date();
		//expires.setTime(now.getTime()+1000000*3600*24);
		expires.setTime(now.getTime()+1000*expires_delta);
		var expires_str = expires.toGMTString();
		var random_cookie = Math.floor(Math.random()*1000000000).toString(16);
		var cookie = "Set-Cookie: pow_session="+random_cookie+"; expires="+
       expires_str+"; path=/;";
		if(!pow_server.user_id) {
			pow_server.user_id = new Array();
		}
		pow_session_add_data(my_db, random_cookie, expires.getTime(), "");
		pow_header(cookie);
	} else {
		var m1 = cook.match(/pow_session=(.*)/);
		random_cookie = m1[1];
	}
	pow_server.SESSION_ID = random_cookie;
	// END COOKIE

	pow_session_run_gc(my_db);

	// SET SESSION FROM data in DB
	var sess_data = pow_session_get_data(my_db, random_cookie);
	if(sess_data) {
		var data_splits = sess_data.split(";");
		for(var i = 0 in data_splits) {
			var val_splits = data_splits[i].split(/=/);
			if(val_splits[0]) {
				var key = val_splits[0].replace(/_EQUALS_/g,"=");
				key = key.replace(/_SEMICOLON_/g,";");
				var val = val_splits[1].replace(/_EQUALS_/g,"=");
				val = val.replace(/_SEMICOLON_/g,";");
				pow_server.SESSION[key] = val;
			}
		}
	}
}

/*
 *
 * pow_session_add_data() 
 * Add session data for cookie-ed user
 *
 */

function pow_session_add_data( pdb, user_id, date, data ) {
	 var hasdata = pow_session_get_data(pdb,user_id);
	 if(!hasdata) {
		if(data) {
   		pdb.exec("INSERT INTO sessions (user_id, expires, data) VALUES ('"+user_id+"','"+date+"','"+data+"')");
		}
	} else {
		if(date) {
   		pdb.exec("UPDATE sessions set data='"+data+"', expires='"+date+"' where user_id='"+user_id+"';");
		} else {
   		pdb.exec("UPDATE sessions set data='"+data+"' where user_id='"+user_id+"';");

		}
	}
}

/*
 *
 * pow_session_init_db() 
 * Initializes session DB
 *
 */

function pow_session_init_db() {
  try {
    var pdb = new pow_DB("pow_sessions");
    pdb.exec("CREATE TABLE sessions (user_id varchar(50), expires int, data blob)");
  } catch  (e) {
    // noop
  }
	return pdb;
}

/*
 *
 * pow_session_get_data() 
 * Return data set for cookie
 *
 */

function pow_session_get_data(pdb, user_id) {
  var results = pdb.exec("SELECT * FROM sessions WHERE user_id='"+user_id+"'");
	if(results[0]) {
		return results[0][2];
	}
}

/*
 *
 * pow_session_run_gc() 
 * Garbage collector, always a chance to start
 *
 */

function pow_session_run_gc(pdb) {
	// ONE in TWENTY chance of collecting garbage
	var rand = Math.floor(Math.random()*20);
	if(rand == 1) {
		var now = new Date();
  	var results = pdb.exec("DELETE FROM sessions WHERE expires < "+now.getTime());
	}
}

